﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Linq;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Drawing;

namespace FileRenamer
{
	public partial class main : Form
	{
		private Regex tag = new Regex("<(\\w+)(?::([^>,]+))?(?:\\,([^>,]+))?>", RegexOptions.IgnoreCase);
		private char[] invalid = new char[] { '\\', '/', ':', '*', '?', '\"', '>', '<', '|', '\t' };

		public main()
		{
			InitializeComponent();

			files.ListViewItemSorter = new Common.ListViewColumnSorter();
			files.ColumnClick += new ColumnClickEventHandler(Common.ListViewHelper.ListView_ColumnClick);
			editText.Left = 5000;
			files.MouseDoubleClick += delegate {
				if (files.SelectedItems.Count == 1)
				{
					editText.Text = files.SelectedItems[0].SubItems[1].Text;
					editText.Left = files.Columns[0].Width + 2;
					editText.Top = files.GetItemRect(files.SelectedItems[0].Index).Top + 40;
					editText.Width = files.Columns[1].Width;
					editText.Tag = editText.Text;
					editText.Focus();
					editText.SelectAll();
				}
			};
			b_Execute.Enabled = false;
		}

		private void b_AddFolder_Click(object sender, EventArgs e)
		{
			FolderBrowserDialog d = new FolderBrowserDialog { Description = "选择包含要重命名的文件的目录:" };
			if (d.ShowDialog() == DialogResult.OK)
			{
				DirectoryInfo di = new DirectoryInfo(d.SelectedPath);
				addFilesToList(di.GetFiles());
			}
		}

		private void b_AddFiles_Click(object sender, EventArgs e)
		{
			OpenFileDialog d = new OpenFileDialog { Multiselect = true, Title = "选择要重命名的文件:" };
			if (d.ShowDialog() == DialogResult.OK)
				addFilesToList((from x in d.FileNames select new FileInfo(x)).ToArray());
		}

		private List<string> all = new List<string>();

		private void addFilesToList(FileInfo[] files)
		{
			foreach (FileInfo file in files)
			{
				if (all.IndexOf(file.FullName) == -1)
				{
					this.files.Items.Add(new ListViewItem(new string[] { file.Name, "" }) { Tag = file, ToolTipText = file.FullName });
					all.Add(file.FullName);
				}
			}
			preview();
		}

		private void b_Remove_Click(object sender, EventArgs e)
		{
			foreach (ListViewItem item in files.SelectedItems)
			{
				all.Remove((item.Tag as FileInfo).ToString());
				files.Items.Remove(item);
			}
		}

		public rules.rule Rule = new rules.rule { Method = rules.Methods.Regex, From = "", To = "" };

		private void b_Setting_Click(object sender, EventArgs e)
		{
			rules r = new rules(this);
			if (r.ShowDialog(this) == DialogResult.OK)
			{
				Rule = r.Rule;
				preview();
			}
		}

		private void preview()
		{
			b_Execute.Enabled = false;

			#region 解析替换规则
			switch (Rule.Method)
			{
				case rules.Methods.Regex:
					try
					{
						Regex r = new Regex(Rule.From);
						foreach (ListViewItem item in files.Items)
						{
							item.SubItems[1].Text = r.Replace(item.SubItems[0].Text,
								execRegex(item.Index, item.Tag as FileInfo, Rule.To)).Trim();
						}
					}
					catch (Exception)
					{
						MessageBox.Show("解析正则时发生了错误");
						return;
					}
					break;
					
				case rules.Methods.Userdef:
					try
					{
						foreach (ListViewItem item in files.Items)
						{
							item.SubItems[1].Text = tag.Replace(Rule.From,
								new MatchEvaluator(x => execUserDef(item.Index, item.Tag as FileInfo, x.Groups[1].Value, x.Groups[2].Value, x.Groups[3].Value))).Trim();
						}
					}
					catch (Exception)
					{
						MessageBox.Show("未能解析此表达式");
						return;
					}
					break;
			}
			#endregion

			#region 检查目标文件名
			List<string> newfiles = new List<string>(), oldfiles = new List<string>();
			bool isok = true;
			foreach (ListViewItem item in files.Items)
			{
				string newname = item.SubItems[1].Text.TrimEnd();
				if (newname.IndexOfAny(invalid) > -1)
				{
					item.BackColor = Color.Red;
					isok = false;
					MessageBox.Show("根据命名规则, 生成的文件名中包含非法字符.\n\n路径及文件名不能包含字符:\n" + string.Join(" ", invalid.Select(x => x.ToString()).ToArray<string>()));
					//b_Setting_Click(null, null);
					return;
				}

				FileInfo file = item.Tag as FileInfo;
				newname = Path.Combine(file.DirectoryName, newname);
				if (newfiles.Contains(newname))
				{
					item.BackColor = Color.Orange;
					isok = false;
					MessageBox.Show("根据命名规则, 列表中将生成一些相同的文件名.", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
					//b_Setting_Click(null, null);
					return;
				}

				item.BackColor = Color.LightGreen;
				newfiles.Add(newname);

				//item.BackColor = file.FullName != newname ? System.Drawing.Color.White : System.Drawing.Color.LightYellow;
			}
			#endregion

			b_Execute.Enabled = isok;
		}

		#region 新文件名处理
		private string execRegex(int index, FileInfo file, string input)
		{
			return tag.Replace(input, new MatchEvaluator(x => execUserDef(index, file, x.Groups[1].Value, x.Groups[2].Value, x.Groups[3].Value))); ;
		}
		private string execUserDef(int index, FileInfo file, string a , string b, string c)
		{// <name>, <ext:0>, <size:unit>, <index:start,len>, <now:format>, <rand:len>
			switch (a.ToLower())
			{
				case "name":
					string name = file.Name.Replace(file.Extension, "");
					return string.IsNullOrEmpty(c) ? name.Substring(int.Parse("0" + b)) : name.Substring(int.Parse(b), int.Parse(c));
				case "ext":
					return file.Extension.ToLower().Substring(string.IsNullOrEmpty(b) || Convert.ToBoolean(b) ? 0 : 1);
				case "size":
					return filesize(file.Length, b.ToUpper());
				case "index":
					string iv = new string('0', 10) + (index + int.Parse(b)).ToString();
					return iv.Substring(iv.Length - int.Parse(c));
				case "now":
					return DateTime.Now.ToString(b);
				case "rand":
					string lv = new string('0', 10) + new Random(index).Next().ToString();
					return lv.Substring(lv.Length - int.Parse(b));
				case "mp3":
					return musicInfo(file, b);
				default:
					return a;
			}
		}
		private string musicInfo(FileInfo file, string tag)
		{
			Common.Mp3TagID3V1 id3 = new Common.Mp3TagID3V1(file.FullName);
			switch (tag)
			{
				case "title": return id3.Title;
				case "album": return id3.Album;
				case "artist": return id3.Artist;
				case "track": return id3.Track;
				case "genre": return id3.Genre;
				default: return tag;
			}
		}
		private string filesize(long bytes, string unit)
		{
			switch (unit)
			{
				case "K":
				case "KB":
					return Convert.ToInt32(bytes / 1024).ToString() + unit;
				case "M":
				case "MB":
					return Convert.ToInt32(bytes / 1024 / 1024).ToString() + unit;
				case "G":
				case "GB":
					return Convert.ToInt32(bytes / 1024 / 1024 / 1024).ToString() + unit;
				default:
					return unit;
			}
		}
		#endregion

		#region 列表菜单处理
		private void menu_Opening(object sender, System.ComponentModel.CancelEventArgs e)
		{
			if (files.SelectedItems.Count == 0) { e.Cancel = true; return; }
			m_MoveTop.Enabled = m_MoveUp.Enabled = (files.SelectedItems[0].Index > 0);
			m_MoveBottom.Enabled = m_MoveDown.Enabled = (files.SelectedItems[files.SelectedItems.Count - 1].Index < files.Items.Count - 1);
		}

		private void m_MoveTop_Click(object sender, EventArgs e)
		{
			List<ListViewItem> items = new List<ListViewItem>();
			for (int i = files.SelectedItems.Count - 1; i >= 0; i--)
			{
				items.Add(files.Items.Insert(0, files.SelectedItems[i].Clone() as ListViewItem));
				files.SelectedItems[i].Remove();
			}
			foreach (var item in items) item.Focused = item.Selected = true;
			preview();
		}

		private void m_MoveBottom_Click(object sender, EventArgs e)
		{
			List<ListViewItem> items = new List<ListViewItem>();
			foreach (ListViewItem item in files.SelectedItems)
			{
				items.Add(files.Items.Add(item.Clone() as ListViewItem));
				item.Remove();
			}
			foreach (var item in items) item.Focused = item.Selected = true;
			preview();
		}

		private void m_MoveUp_Click(object sender, EventArgs e)
		{
			List<ListViewItem> items = new List<ListViewItem>();
			foreach (ListViewItem item in files.SelectedItems)
			{
				items.Add(files.Items.Insert(item.Index - 1, item.Clone() as ListViewItem));
				item.Remove();
			}
			foreach (var item in items) item.Focused = item.Selected = true;
			preview();
		}

		private void m_MoveDown_Click(object sender, EventArgs e)
		{
			List<ListViewItem> items = new List<ListViewItem>();
			for (int i = files.SelectedItems.Count - 1; i >= 0; i--)
			{
				items.Add(files.Items.Insert(files.SelectedItems[i].Index + 2, files.SelectedItems[i].Clone() as ListViewItem));
				files.SelectedItems[i].Remove();
			}
			foreach (var item in items) item.Focused = item.Selected = true;
			preview();
		}
		#endregion

		private void b_Execute_Click(object sender, EventArgs e)
		{
			if (MessageBox.Show("确定执行重命名?", "操作确认", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.Cancel) return;
			
			List<string> newfiles = new List<string>(), oldfiles = new List<string>();

			foreach (ListViewItem item in files.Items)
			{
				FileInfo file = item.Tag as FileInfo;
				string newname = Path.Combine(file.DirectoryName, item.SubItems[1].Text.Trim());

				newfiles.Add(newname);
				oldfiles.Add(file.FullName);
			}

			var items = files.Items;
			int count = items.Count, i = 0;

			try
			{
				for (; i < count; i++)
				{
					File.Move(oldfiles[i], newfiles[i]);
					items[i].ForeColor = System.Drawing.Color.Green;
				}

				MessageBox.Show(count.ToString() + "个文件重命名成功");
				items.Clear();
				all.Clear();
				saveOptions(Rule);
			}
			catch (Exception)
			{
				if (MessageBox.Show("在第 " + (i + 1).ToString() + " 个文件执行重命名时发生了错误. 是否撤销已重命名的操作?",
					"提示", MessageBoxButtons.YesNo) == DialogResult.Yes)
				{
					items[i--].ForeColor = System.Drawing.Color.Red;

					for (; i >= 0; i--)
					{
						File.Move(newfiles[i], oldfiles[i]);
						items[i].ForeColor = System.Drawing.Color.Blue;
					}
				}
			}
		}

		private void saveOptions(rules.rule rule)
		{
			Properties.Settings setting = Properties.Settings.Default;

			if (rule.Method == rules.Methods.Userdef)
			{
				List<string> userdef = setting.UserDef.Split('\n').ToList();
				userdef.Remove(rule.From);
				userdef.Insert(0, rule.From);
				setting.UserDef = string.Join("\r\n", userdef.ToArray()).Trim();
			}
			else
			{
				List<string> regfrom = setting.RegFrom.Split('\n').ToList();
				regfrom.Remove(rule.From);
				regfrom.Insert(0, rule.From);
				setting.RegFrom = string.Join("\r\n", regfrom.ToArray()).Trim();

				List<string> regto = setting.RegTo.Split('\n').ToList();
				regto.Remove(rule.To);
				regto.Insert(0, rule.To);
				setting.RegTo = string.Join("\r\n", regto.ToArray()).Trim();
			}

			setting.Save();
		}

		private void 管理规则ToolStripMenuItem_Click(object sender, EventArgs e)
		{
			new display().ShowDialog();
		}

		private void editText_Leave(object sender, EventArgs e)
		{
			if (files.SelectedItems.Count == 0) return;
			if (sender.ToString() != "false") files.SelectedItems[0].SubItems[1].Text = editText.Text;
			files.Focus();
			editText.Left = 5000;
		}

		private void editText_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
		{
			if (e.KeyCode == Keys.Enter)
			{
				files.Focus();
			}
			else if (e.KeyCode == Keys.Escape)
			{
				editText.Text = editText.Tag.ToString();
				files.Focus();
			}
		}
	}
}
